#include "CullNodeVisitor.h"
#include "Node.h"
#include "GeoNode.h"
#include "GroupNode.h"
namespace sl
{
	CullNodeVisitor::CullNodeVisitor(void)
	{
	}

	CullNodeVisitor::~CullNodeVisitor(void)
	{
	}

	void CullNodeVisitor::Apply( sl::Node * pNode )
	{
		if(IsCulled( pNode )) return;

		DrawAttributeSet * pAtrributeSet = pNode->GetAtrributeSet();
		if ( pAtrributeSet ) PushAtrributeSet( pAtrributeSet );
		
		// we can set the cull_back function
		if ( pAtrributeSet ) PopAtrributeSet( pAtrributeSet );
	}

	void CullNodeVisitor::Apply( sl::GeoNode * pGeoNode )
	{
		if ( IsCulled( pGeoNode ) ) return;

		sl::Matrix4 ViewMatrix = m_rCamera->GetViewMatrix();
		DrawAttributeSet * pAtrributeSet = pGeoNode->GetAtrributeSet();
		if ( pAtrributeSet ) PushAtrributeSet( pAtrributeSet );

		for ( u32 i = 0; i < pGeoNode->GetDrawableNum(); ++i )
		{
			sl::Drawable * drawable = pGeoNode->GetDrawableWithPosition( i );
			sl::BoundingBox * bb = drawable->GetBound();

			if ( IsCulled( bb ) )
			{
				float distanse = makeDistanse( bb->GetCenter(), ViewMatrix );
				AddDrawableAndDepth( drawable, ViewMatrix, distanse );
			}
		}

		if ( pAtrributeSet ) PopAtrributeSet( pAtrributeSet );
	}

	void CullNodeVisitor::Apply( sl::GroupNode * pGroupNode )
	{
		
	}

	void CullNodeVisitor::SetCurrentCamera( sl::Camera * pCamera )
	{ 
		m_rCamera = pCamera;
	}

	bool CullNodeVisitor::IsCulled( sl::Node * pNode )
	{/*
		if ( m_rFrustum )
			return !m_rFrustum->IsContains(  );*/
		return false;
	}

	bool CullNodeVisitor::IsCulled( sl::BoundingBox * pBound )
	{

		if ( m_rFrustum.valid() )
			return !m_rFrustum->IsContains( pBound );
		return false;
	}

	void CullNodeVisitor::PushAtrributeSet( sl::DrawAttributeSet * pAttributeSet )
	{

	}

	void CullNodeVisitor::PopAtrributeSet( sl::DrawAttributeSet * pAtrributeSet )
	{

	}

	void CullNodeVisitor::AddDrawableAndDepth( sl::Drawable * drawable, sl::Matrix4 & view, float depth )
	{
		sl::Matrix4 project = m_rCamera->GetProjectMatrix();
		m_rStateGraph->AddStateLeaf( new StateLeaf( drawable, &project, &view, depth ));
	}

	void CullNodeVisitor::MakeFrustum( sl::Camera * pCamera  )
	{
		if ( !m_rFrustum )
			m_rFrustum = new Frustum();
		m_rFrustum->GenerateFrustumWithWiewAndPorject( m_rCamera->GetProjectMatrix(), m_rCamera->GetViewMatrix() );
	}

	float CullNodeVisitor::makeDistanse( Vector3 & left, Matrix4 & right )
	{
		return -(left[0] * right[0][2] + left[1] * right[1][2] + left[2] * right[2][2] + right[3][2]);
	}
}
